퍼셉트론

분류의 문제는 앞장에서 설명한 곡선 맞춤 문제와 함께 기계학습에서 많이 다루어지는 문제다. 2차원 평면 위에 놓인 점들이 있다고 하자. 이 경우, 적당한 곡선을 택해서, 각 점이 곡선의 위인지 아래인지, 혹은 왼쪽인지 오른쪽인지에 따라 두 개의 그룹으로 점들을 나누는 것이 바로 분류 문제다. 예를 들어, 사람들의 키([eqn: x])를 가로축, 몸무게([eqn: y])를 세로축으로 해서 2차원 평면에 여러 사람의 자료를 점으로 표시했다고 해 보자. 이 때, 적당한 곡선 [eqn: y`=`f(x)]을 택해서 [eqn: y`>`f(x)]인 경우에는 과체중, [eqn: y`<`f(x)]의 경우에는 저체중이라고 판정하는 문제가 간단한 분류 문제의 한 예다. 분류 문제에서는 바로 이 곡선 [eqn: y=f(x)]를 찾는 것이 목표가 된다. 가장 간단한 분류 문제는 [eqn: y=f(x)]가 직선인 경우인데, 이 경우 분류를 수행하는 것을 선형 분류기라고 부를 수 있다. 선형 분류기의 대표적인 예가 퍼셉트론(perceptron)이다.

퍼셉트론은 동물의 뇌 안에 있는 신경세포(neuron)와 비슷한 방식으로 작동한다. 먼저, 신경세포의 작동방식에 대해 알아보자. 신경세포는 다른 신경세포와 시냅스라는 구조를 통해 연결되어있다. 시냅스 앞 신경세포(pre-synaptic neuron)에서 발생한 전기 신호는 축삭(axon)을 통해 시냅스로 전달되고, 이 정보는 시냅스를 거쳐 시냅스 뒤 신경세포(post-synaptic neuron)의 수상돌기(dendrite)로 전달된다. 한 신경세포에는 여러 수상돌기가 있고, 각각의 수상돌기는 시냅스를 통해 다른 신경세포의 축삭을 따라 전달된 전기 신호를 받아들인다. 만약, 여러 시냅스를 통해 전달된 전기 신호의 합이 충분히 강하면, 신경세포는 발화(firing)하게 되는데, 이는 신경세포의 세포막 안팎의 전위차가 급격히 양의 값으로 커졌다가 줄어드는 꼴이다. 시냅스 앞 신경세포에서 전달된 정보는 시냅스를 통해 받아들여지는데, 정보가 받아들여지는 세기는 시냅스마다 다를 수 있다. 위의 과정을 단순화해서 신경세포의 발화를 기술하는 모형이 McCluch-Pitts 모형이다. 그림에서 [eqn: S _{1} ,`S _{2} ,`S _{3}]는 각각의 시냅스 앞 신경세포의 상태를 뜻하며 발화하지 않은 상태를 0, 발화한 상태를 1로 표시한다. 예를 들어, 그림의 세 시냅스 앞 신경세포 중 첫 번째 신경세포만 발화했다면 [eqn: S _{1} =1,`S _{2} =S _{3} =0]이다. 그림에서 [eqn: W _{ij}]는 시냅스 앞 신경세포 [eqn: j]와 시냅스 뒤 신경세포 [eqn: i] 사이의 시냅스의 연결강도를 뜻한다. 즉, [eqn: W _{ij} S_j]는 [eqn: j]로부터 시냅스를 거쳐 [eqn: i]에 전달된 정보의 양이다. 시냅스 후 신경세포 [eqn: i]는 각각의 신경세포 ([eqn: j = 1,2,3]) 각각과 연결된 시냅스를 거쳐 입력된 정보를 취합하고([eqn: sum_j W_ij S_j])는 그 총합이 주어진 문턱값 [eqn: b]보다 큰 경우에는 발화([eqn: S_i = 1])하고, 작은 경우에는 발화하지 않는다([eqn: S_i = 0]). 입력의 총합에 따라 발화 여부를 결정하는데 관여하는 함수를 활성화 함수(activation function)라 부른다. 위에서 설명한 방식의 경우에는 활성함수는 간단한 계단 모양의 함수가 되는데, 물리학에서 자주 등장하는 헤비사이드 계단함수(Heaviside step function) [eqn: Theta(x)]를 이용하면, [eqn: S_i = Theta ( sum_j W_ij S_j - b )]의 수식으로 전 과정을 간단히 표현할 수 있다. 바로, 여러 신경세포로부터의 입력정보를 취합해 발화여부를 결정하는 신경세포를 단순화해서 기술하는 McCluch-Pitts 모형이다.

기계학습 분야의 퍼셉트론의 작동방식이 바로 위에서 설명한 신경세포 모형과 닮았다. 그림과 같은 연결 구조를 생각해보자. 그림의 가장 왼쪽의 두 노드는 입력 층, 오른쪽 두 노드는 출력 층에 해당하고, 입력된 정보는 왼쪽에서 오른쪽 방향으로, 즉 입력 층에서 출력 층의 방향으로 전달된다. 정보가 항상 입력 층에서 출력 층의 한 방향으로 전달되므로, 이런 방식으로 작동하는 퍼셉트론을 순방향(feedforward) 퍼셉트론이라고 한다. 앞에서 설명한 신경세포 모형과 달리 퍼셉트론에서는 각 노드가 가질 수 있는 값은 0과 1이라는 정수 값이 아닌 연속적인 실수 값도 허용한다. 하지만, 작동방식은 위에서 설명한 McCluch-Pitts 모형과 아주 흡사하다. 입력 층의 1번 노드에 입력된 정보 [eqn: x_1]은 가중치(weight)(신경세포 모형의 시냅스의 연결강도에 해당) [eqn: W_11]을 거쳐 출력 층의 1번 노드에 전달되고, 마찬가지로 입력 층 2번 노드의 정보 [eqn: x_2]는 [eqn: W_12]를 거쳐 출력 층 1번 노드에 전달된다. 출력 층 1번 노드에는 이렇게 두 입력 층 노드의 정보가 가중치에 곱해진 후 취합되는데, 편의상 이 값에 앞에서 소개한 문턱값에 해당하는 양을 더해서 [eqn: v_1]이라 부르자. 즉,  [eqn: v _{1} =W _{11} x _{1} +W _{12} x _{2} + b_1]이다.(앞의 신경세포의 경우와 비교하면 편의상 [eqn: b]의 부호를 바꿔 적었음에 유의하라. 출력 노드의 상태가 변하는 기준값이 0에서 [eqn: b]로 바뀌게 되므로 [eqn: b]를 편향 혹은 바이어스(bias)라 부른다.) 출력 층 1번 노드가 출력하는 값 [eqn: y_1]은 [eqn: v_1]에 의해 결정되는데, 이를 결정하는 것이 앞에서도 등장했던 활성화 함수 [eqn: phi(v)]다. 먼저, 가장 간단한 활성화 함수의 예로서 McCluch-Pitts 모형에서와 마찬가지로 [eqn: phi (v)= THETA (v)]를 택하면, [eqn: y _{1} = phi (v _{1} )= Theta( v _{1} )= Theta( W _{11} x _{1} +W _{12} x _{2} +b _{1})]으로 적을 수 있다. 마찬가지의 과정을 거치면 출력 층의 2번째 노드의 출력 값은 [eqn: y _{2} = phi (v _{2} )= Theta( v _{2} ) = Theta( W _{21} x _{1} +W _{22} x _{2} +b _{2} )]로 적힌다. 지금까지는 입력 층과 출력 층에 각각 노드가 두 개만이 있는 경우를 생각했지만, 위의 과정은 노드의 개수가 임의로 주어져 있는 경우로 쉽게 확장이 가능하다. 이 경우 출력 층의 [eqn: i]번째 노드의 출력 값은 [eqn: y _{i} = phi (v _{i} )= Theta( v _{i} ) = Theta( sum _{j} ^{} W _{ij} `x _{j} +b _{i} )]로 적을 수 있다.

순방향 퍼셉트론을 분류기로 이용하는 간단한 예로서 논리곱(AND)을 생각해보자. 논리곱 연산은 두 입력 값이 모두 참(True)이면 결과가 참이지만, 그 밖의 모든 경우에는 거짓(False)을 결과로 주는 연산이다. 참을 1로, 거짓을 0으로 표시하면 논리곱 연산은 다음과 같은 표의 형태로 정리해 적을 수 있다. 자, 논리곱을 퍼셉트론을 이용해 구현해 보자. 위의 그림에서 생각한 경우보다 상황은 더 간단해서 출력 층의 노드는 딱 한 개만 필요하다. 즉, [eqn: y= phi (v)= Theta(v)= Theta( W _{1} `x _{1} +W _{2} `x _{2`} +b )]의 형태로 적을 수 있다. 어떻게 가중치 [eqn: W_1 , W_2], 그리고 편향값 [eqn: b]를 체계적으로 정하는 지에 대한 설명은 이어지는 9장으로 미루고, [eqn: W _{1} =W _{2} =1,`b=-1.5]로 고정하면 논리곱 연산이 구현됨을 아래 프로그램을 실행해서 확인해보자.

import numpy as np, matplotlib.pyplot as plt

def phi(x):

return (  np.heaviside(x, 0.0) )

x = np.array( [[1,1], [1,0], [0,1], [0,0]] )

W = np.array( [1,1])

b = -1.5

for n in range(4):

v = np.sum(W*x[n]) + b

y = phi(v)

print(x[n], y)

위 프로그램에서는 numpy의 heaviside(x,a)를 이용했는데, 이 함수는 만약 x > 0 이면 1의 값을, x < 0이면 0의 값을 돌려주며, 만약 x의 값이 정확히 0이면 a의 값을 돌려준다. 참고로 numpy에는 sign(x) 함수도 있는데 이를 이용해 헤비사이드 계단함수를 구현할 수도 있다. 위 프로그램에서 x = np.array( [[1,1], [1,0], [0,1], [0,0]] )는 네 개의 요소로 구성된 배열 x를 만든다. 즉, x[0] = [1,1], x[1] = [1,0], x[2] = [0,1], x[3] = [0,0]이다. 마찬가지로  W = np.array([1,1])도 배열 W를 만드는데, W[0] = 1, W[1] = 1이다. 다음으로 [eqn: v= W _{1} `x _{1} +W _{2} `x _{2`} +b]를 구하기 위해 먼저 np.sum(W*x[n])를 이용했다. W*x[n]는 각각의 요소끼리 곱하는 연산이고, sum은 이렇게 얻어진 요소를 모두 더해 총합([eqn: W _{1} `x _{1} +W _{2} `x _{2`}])을 구하는 함수이다.

== 예제 1==

위 프로그램에서 numpy의 heaviside()를 sign()을 이용해 바꿔보시오. 또, v = np.sum(W*x[n]) + b를 np.dot()를 이용해 바꿔 작성하고 결과를 확인하시오. 또, 함수 matmul() 혹은 연산자 @를 이용해 모든 가능한 네 개의 입력에 대한 계산을 for loop을 이용하지 않고 한 번에 수행하도록 프로그램을 작성해 보시오.

== 예제 2==

[eqn: W_1 = W_2 = 1, b = -1.5]을 이용하면 올바른 논리곱 값이 출력되는 이유를 설명하시오. 논리합(OR)을 올바로 출력하기 위한 [eqn: W_1 , W_2 , b]의 값을 생각하고, 이를 이용해 논리합 연산을 수행하는 퍼셉트론 프로그램을 작성하시오.

== 과제 1==

배타적 논리합(XOR) 연산은 두 입력 값이 같을 때 [즉, [eqn: (x_1 , x_2 ) = (1,1)] 또는 [eqn: (0,0)]]  0을, 두 입력 값이 다를 때  [즉, [eqn: (x _{1} ,x _{2} )=(1,0)] 또는 [eqn: (0,1)]] 1을 출력한다. XOR을 수행하는 선형 분류기는 불가능함을 설명하시오.

== 과제 2 ==

7장의 예제 1에서 각 물고기의 길이와 무게를 [eqn: y = ln M], [eqn: x = ln L]로 바꿔 적고, [eqn: y=a+bx]의 형태의 곡선맞춤을 통해 [eqn: a, b]의 값을 구한 바 있다. 물고기의 길이와 무게가 [eqn: y > a+bx]를 만족하면 1의 값을, 아니면 0의 값을 출력하는 프로그램을 퍼셉트론을 이용해 작성하시오. 1과 0의 값을 출력하는 물고기 개체의 비율은 각각 얼마인가? 전체 자료에서 확률변수를 [eqn: z = y-(a+bx)]로 택하고, [eqn: z]의 확률분포를 그래프로 그리고 이를 정규분포 곡선에 곡선 맞춤하시오. 이 과정에서 정규분포의 평균값 [eqn: bar z]와 표준편차 [eqn: sigma]를 구하시오.

== 과제 3 ==

위 과제 2의 문제를 출력 층에 두 개의 노드를 이용해 다른 방식으로 구현하려고 한다. [eqn: z > bar z + sigma]이면 출력 노드 두 개가 (1,1)을, [eqn: z < {bar{z}} - sigma]이면 (0,0)을, 그리고 [eqn: bar z - sigma < z < bar z + sigma]이면 (0,1)을 출력하는 퍼셉트론 프로그램을 작성하시오. 각각의 경우를 과체중, 저체중, 그리고 정상체중이라고 하고, 각각의 경우에 해당하는 물고기의 비율을 구하시오.